iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
生成式 AI

阿,又是一個RAG系列 第 2

Day1-get_context_from_YT

  • 分享至 

  • xImage
  •  

TL;DR

我們今天兜了三個小工具,
程式在這裡的days/day1
python get_transcription.py
是從自帶字幕的youtube連結獲取字幕,存成txt檔
python yt_download.py
如果本身沒有自帶字幕的話,就先把影片的音訊檔載下來
python whisper_audio2txt.py
然後用whisper轉成文字檔
結果在專案目錄下的data下的lee.txt和hung.json

Situation:

我們想要替RAG系統獲得成對的(context, question, answer)供後續方法論的驗證

Task:

當前的子任務是:先想辦法獲得context,我們後續再prompt llm從context生成(Q, A) pair

Action:

這部分的想法是從youtube影片獲取,那這又區分成兩個case:
case1: 當youtube影片有提供字幕的時候,我們直接把字幕載下來
case2: 當youtube影片本身沒有提供字幕的時候,我們可以把音訊檔下載下來,丟給whisper
我們準備了兩個youtube的連結:

套件們

pip install youtube-transcript-api
  • yt_dlp: 這個是當youtube本身沒有字幕的時候,我們要用這個套件把音訊檔載下來
sudo apt install -y ffmpeg
pip install yt-dlp
pip install faster-whisper

開始寫code

首先是有字幕的case

  1. 首先是一個helper function 用來把youtube的網址轉成video_id(transcript_api要用)
    def _extract_video_id(url: str) -> str:
     p = urlparse(url)
     if p.netloc.endswith("youtu.be"):
         return p.path.lstrip("/").split("/")[0]
     q = parse_qs(p.query)
     if "v" in q:
         return q["v"][0]
     m = re.search(r"/(shorts|embed)/([A-Za-z0-9_-]{6,})", p.path)
     if m:
         return m.group(2)
     raise ValueError("無法從網址解析出 YouTube 影片 ID。")
    
  2. 一個判斷影片本身有沒有自帶字幕的函數,沒有就回傳False
    def has_youtube_captions(url: str, preferred_langs=None) -> bool:
     from youtube_transcript_api import YouTubeTranscriptApi
     from youtube_transcript_api._errors import TranscriptsDisabled
     from youtube_transcript_api import NoTranscriptFound
    
     video_id = _extract_video_id(url)
     api = YouTubeTranscriptApi()
    
     try:
         tlist = api.list(video_id)  # TranscriptList,可疊代
     except (TranscriptsDisabled, NoTranscriptFound):
         return False
     except Exception:
         return False
    
     if preferred_langs:
         try:
             tlist.find_transcript(preferred_langs)
             return True
         except NoTranscriptFound:
             return False
     else:
         return len(list(tlist)) > 0
    
  3. 字幕的提取:
    def fetch_youtube_captions(
     url: str,
     preferred_langs=None,
     translate_to: str | None = None,
     join_with: str = "\n",
     preserve_formatting: bool = False,
     ) -> str:
     if preferred_langs is None:
         preferred_langs = ["zh-Hant", "zh-TW", "zh-Hans", "zh", "en"]
    
     video_id = _extract_video_id(url)
     from youtube_transcript_api import YouTubeTranscriptApi, NoTranscriptFound
    
     api = YouTubeTranscriptApi()
    
     if translate_to:
         # 先找到一個字幕,再翻譯
         tlist = api.list(video_id)
         try:
             base_t = tlist.find_transcript(preferred_langs)
         except NoTranscriptFound:
             base_t = next(iter(tlist), None)
         fetched = base_t.translate(translate_to).fetch(preserve_formatting=preserve_formatting)
     else:
         fetched = api.fetch(video_id, languages=preferred_langs, preserve_formatting=preserve_formatting)
    
     raw = fetched.to_raw_data()
     lines = [d["text"].strip() for d in raw if d.get("text", "").strip()]
     junk = {"[Music]", "[Applause]", "[Laughter]"}
     return join_with.join([ln for ln in lines if ln not in junk])
    

到這邊看一下結果:

text = fetch_youtube_captions(links[0])
print(text[:30])

'各位同學大家好 我們來上課吧\n剛才只是用Google的VO3'

沒有字幕的case

我們要先用yt_dlp把影片載下來,這個如果安裝上有問題,我覺得就直接去colab上用比較省事
4. 下載mp3

def download_audio(url, output):
    ydl_opts = {
        "format": "bestaudio/best",
        "outtmpl": output,
        "postprocessors": [{
            "key": "FFmpegExtractAudio",
            "preferredcodec": "mp3",
            "preferredquality": "192"
        }]
    }
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])
    return output
    
output_path = 'audio.mp3'
download_audio(links[1], output_path)

到這邊路徑下會有一個audio.mp3檔案,點開來就是影片的語音檔了。
5. 用faster_whisper 把mp3轉txt

from faster_whisper import WhisperModel
from pathlib import Path

def transcribe_faster_whisper(
    audio_file: str | Path,
    model_name: str = "large-v3",
    device: str = "auto",          # "cuda" | "cpu" | "auto"
    compute_type: str = "auto",     # "float16" | "int8_float16" | "int8" | "auto"
    language: str | None = None,    # "zh"、"en"、None=自動偵測
):
    audio_file = str(audio_file)
    model = WhisperModel(model_name, device=device, compute_type=compute_type)
    segments, info = model.transcribe(
        audio_file,
        language=language,
        vad_filter=True,        # 內建 Voice Activity Detection,通常品質更穩
        beam_size=5,
    )
    # 收集所有 segments
    segs = []
    full_text = []
    for seg in segments:
        segs.append({"start": seg.start, "end": seg.end, "text": seg.text})
        full_text.append(seg.text)
    return {
        "text": " ".join(full_text).strip(),
        "segments": segs,
        "language": info.language,
        "language_probability": info.language_probability,
    }

到這邊看一下結果:

zh 0.9990234375
大家平安 我是黃宗霖醫師 在當新手父母的過程當中 寶寶的睡眠這件事情 一直都是我們黃宗霖醫師健康講上 留人區最頻繁被爸媽提出來的問題 而且我自己孩子很小的時候 我曾經腦袋有一種邪惡的一種念頭 就是當寶
dur: 46.231650829315186 sec

可以看到比如人名的辨識會有問題,沒有標點,還有一些特殊的詞彙也做不好,但也就堪用吧!

關於faster_whisper的環境問題

如果faster_whisper執行有問題的話,通常是因為找不到cudnn
我遇到的是顯示:

Unable to load any of {libcudnn_ops.so.9.1.0, libcudnn_ops.so.9.1, libcudnn_ops.so.9, libcudnn_ops.so}
Invalid handle. Cannot load symbol cudnnCreateTensorDescriptor
Aborted (core dumped)

解決:

除了直接安裝cudnn以外:
pip install -U --no-cache-dir nvidia-cudnn-cu12 ctranslate2
還要記得把lib加到LD_LIBRARY_PATH

export LD_LIBRARY_PATH="$(

python - <<'PY'

import os, site

libdirs=[]

for base in site.getsitepackages()+[site.getusersitepackages()]:

    for sub in [("nvidia","cudnn","lib"), ("nvidia","cublas","lib")]:

        d=os.path.join(base, *sub)

        if os.path.isdir(d): libdirs.append(d)

print(":".join(libdirs))

PY

):${LD_LIBRARY_PATH}"

再不然就是改用cpu跑model選tiny其實也不慢
老樣子,本地環境弄不起來的話就可以直接去colab上用省事

Summary:

我們今天兜了兩個小工具,
一個是從自帶字幕的youtube連結獲取字幕,準備當成上下文
一個是當youtube影片本身沒有附字幕的時候,我們把它下載下來,再用whisper轉成文字檔

  • 後續會把程式更新到github

延伸:

  • 明天來解決載下來的逐字稿太長的問題
  • 這是第一篇帶有程式的文章,寫完才發現應該要縮減程式碼的篇幅,不然簡單的工具也會把文章拖得很長,這個就下篇改進。
  • 在找資料的過程中發現openai官方的api有一些whisper的cookbook可以參考,可以prompt whisper來解決一些人名識別的問題,- 如果後面沒東西寫了也許可以試著提升一下whisper這條路。

上一篇
day0: index
下一篇
Day2-RagDatasetGenerator
系列文
阿,又是一個RAG3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言